
/**什么是闭包?
 * 闭包就是能够读取其他函数内部变量的函数，在js中，只有函数内部的子函数才能读取内部变量，因此可以把闭包简单理解成“定义在一个函数内部的函数”。
 * 个人理解，闭包就是函数中的函数（其他语言不能函数再套函数），里面的函数可以访问外面函数的变量，外面的变量是这个函数内部的一部分
*/

/**
 * 闭包形成的条件
 * 1、函数嵌套
 * 2、内部函数引用外部函数的局部变量
 */

//例题1
var inner;
function outer(){
    var a = 250;
    inner = function(){
        console.log(a);//这个函数虽然在外面执行，但能够记忆住定义时的那个作用域，a是250
    }
}
outer();
var a = 300;
inner();//一个函数在执行的时候，找闭包里面的变量，不会理会当前作用域。 // 250

//例题2
function outer1(x){
    function inner1(y){
        console.log(x+y);
    }
  return inner1;
}
var inn = outer1(3);//数字3传入outer函数后，inner函数中x便会记住这个值
inn(5);//当inner函数再传入5的时候，只会对y赋值，所以最后弹出8

/**
 * 闭包的内存泄露
 * 栈内存提供一个执行环境，即作用域，包括全局作用域和私有作用域
 * 全局作用域 —— 只有当页面关闭的时候全局作用域才会销毁
 * 私有的作用域 —— 只有函数执行才会产生
 * 
 * 一般情况下，函数执行会形成一个新的私有的作用域，当私有作用域中的代码执行完成后，我们当前作用域都会主动的进行释放和销毁。
 * 但当遇到函数执行返回了一个引用数据类型的值，并且在函数的外面被一个其他的东西给接收了，这种情况下一般形成的私有作用域都不会销毁
 */

function fn(){
    var num = 100;
    return function(){}
}
var f = fn();//fn执行形成的这个私有的作用域就不能再销毁了
/**
 * 上面这段代码，fn函数内部的私有作用域会被一直占用的，发生了内存泄漏。所谓内存泄漏指任何对象在您不再拥有或需要它之后仍然存在。闭包不能滥用，否则会导致内存泄漏，影响网页的性能。
 * 闭包使用完了后，要立即释放资源，将引用变量指向null
 */

 function outer2(){
    var num = 0;//内部变量
    return function add(){//通过return返回add函数，就可以在outer2函数外访问了
        num++;//内部函数有引用，作为add函数的一部分了
        console.log(num);
    };
}
var func1 = outer2();
func1();//实际上是调用add函数， 输出1
func1();//输出2 因为outer函数内部的私有作用域会一直被占用
var func2 = outer2();
func2();// 输出1  每次重新引用函数的时候，闭包是全新的。
func2();// 输出2

/**闭包的作用
 * 1、可以读取函数内部的变量
 * 2、可以使变量的值长期保存在内存中，生命周期比较长
 * 3、可以用来实现js模块
 * js模块：具有特定功能的js文件，将所有的数据和功能都封装在一个函数内部（私有的），只需要通过模块暴露的对象调用方法来实现对应的功能
 */

//index.html文件
{/* <script type="text/javascript" src="myModule.js"></script> */}
{/* <script type="text/javascript">
  myModule2.doSomething()
  myModule2.doOtherthing()
</script> */}

//myModule.js文件
(function () {
  var msg = 'Beijing'//私有数据
  //操作数据的函数
  function doSomething() {
    console.log('doSomething() '+msg.toUpperCase())
  }
  function doOtherthing () {
    console.log('doOtherthing() '+msg.toLowerCase())
  }
  //向外暴露对象(给外部使用的两个方法)
  exports.myModule2 = {
    doSomething: doSomething,
    doOtherthing: doOtherthing
  }
})()
